Põhjalik juhend puu läbimise algoritmidele: sügavutiotsing (DFS) ja laiutiotsing (BFS). Õpi nende põhimõtteid, rakendamist, kasutusjuhtumeid ja jõudlusomadusi.
Puu läbimise algoritmid: sügavutiotsing (DFS) vs. laiutiotsing (BFS)
Arvutiteaduses on puu läbimine (tuntud ka kui puuotsing või puus kõndimine) protsess, kus külastatakse (uuritakse ja/või uuendatakse) iga sõlme puu andmestruktuuris täpselt üks kord. Puud on fundamentaalsed andmestruktuurid, mida kasutatakse laialdaselt erinevates rakendustes, alates hierarhiliste andmete (nagu failisüsteemid või organisatsioonilised struktuurid) esitamisest kuni tõhusa otsingu ja sorteerimisalgoritmide hõlbustamiseni. Puu läbimise mõistmine on oluline sellega efektiivseks töötamiseks.
Kaks peamist lähenemisviisi puu läbimisele on sügavutiotsing (DFS) ja laiutiotsing (BFS). Iga algoritm pakub erinevaid eeliseid ja sobib erinevat tüüpi probleemidele. See põhjalik juhend uurib nii DFS-i kui ka BFS-i üksikasjalikult, hõlmates nende põhimõtteid, rakendamist, kasutusjuhtumeid ja jõudlusomadusi.
Puu andmestruktuuride mõistmine
Enne läbimise algoritmidesse sukeldumist vaatame lühidalt üle puu andmestruktuuride põhitõed.
Mis on puu?
Puu on hierarhiline andmestruktuur, mis koosneb servadega ühendatud sõlmedest. Sellel on juursõlm (kõige ülemine sõlm) ja igal sõlmel võib olla null või rohkem lapsi. Lapsendeta sõlmi nimetatakse lehtsõlmedeks. Puu peamised omadused on järgmised:
- Juur: Kõige ülemine sõlm puus.
- Sõlm: Element puus, mis sisaldab andmeid ja potentsiaalselt viiteid lapsõlmedele.
- Serv: Ühendus kahe sõlme vahel.
- Vanem: Sõlm, millel on üks või mitu lapsõlme.
- Laps: Sõlm, mis on otse ühendatud teise sõlmega (oma vanemaga) puus.
- Leht: Sõlm, millel pole lapsi.
- Alampuu: Puu, mis on moodustatud sõlmest ja kõigist selle järglastest.
- Sõlme sügavus: Servade arv juurest sõlmeni.
- Puu kõrgus: Suurim sügavus mis tahes sõlmes puus.
Puu tĂĽĂĽbid
Erinevaid puid on mitmeid, millest igaühel on spetsiifilised omadused ja kasutusjuhtumid. Mõned levinumad tüübid on järgmised:
- Binaarpuu: Puu, kus igal sõlmel on kõige rohkem kaks last, mida tavaliselt nimetatakse vasakuks lapseks ja paremaks lapseks.
- Binaarotsingupuu (BST): Binaarpuu, kus iga sõlme väärtus on suurem või võrdne kõigi sõlmede väärtusega selle vasakpoolses alampuus ja väiksem või võrdne kõigi sõlmede väärtusega selle parempoolses alampuus. See omadus võimaldab tõhusat otsingut.
- AVL puu: Isebalanseeruv binaarotsingupuu, mis säilitab tasakaalustatud struktuuri, et tagada logaritmiline ajaline keerukus otsingu, sisestamise ja kustutamise toimingute jaoks.
- Punase-must puu: Teine ise tasakaalustav binaarotsingupuu, mis kasutab tasakaalu säilitamiseks värviomadusi.
- N-aarne puu (või K-aarne puu): Puu, kus igal sõlmel võib olla kõige rohkem N last.
SĂĽgavutiotsing (DFS)
Sügavutiotsing (DFS) on puu läbimise algoritm, mis uurib nii kaugele kui võimalik mööda iga haru enne tagasipöördumist. See seab prioriteediks puu sügavusse mineku enne õdede-vendade uurimist. DFS-i saab rakendada rekursiivselt või iteratiivselt, kasutades pinu.
DFS Algoritmid
DFS-i läbimisi on kolme levinumat tüüpi:
- Keskmine läbimine (Vasak-Juur-Parem): Külastab vasakpoolset alampuud, seejärel juursõlme ja lõpuks parempoolset alampuud. Seda kasutatakse tavaliselt binaarotsingupuude jaoks, kuna see külastab sõlmi sorteeritud järjekorras.
- Eesjärjestuses läbimine (Juur-Vasak-Parem): Külastab juursõlme, seejärel vasakpoolset alampuud ja lõpuks parempoolset alampuud. Seda kasutatakse sageli puu koopia loomiseks.
- Tagajärjestuses läbimine (Vasak-Parem-Juur): Külastab vasakpoolset alampuud, seejärel parempoolset alampuud ja lõpuks juursõlme. Seda kasutatakse tavaliselt puu kustutamiseks.
Rakendamise näited (Python)
Siin on Pythoni näited, mis demonstreerivad iga tüüpi DFS-i läbimist:
class Node:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
# Keskmine läbimine (Vasak-Juur-Parem)
def inorder_traversal(root):
if root:
inorder_traversal(root.left)
print(root.data, end=" ")
inorder_traversal(root.right)
# Eesjärjestuses läbimine (Juur-Vasak-Parem)
def preorder_traversal(root):
if root:
print(root.data, end=" ")
preorder_traversal(root.left)
preorder_traversal(root.right)
# Tagajärjestuses läbimine (Vasak-Parem-Juur)
def postorder_traversal(root):
if root:
postorder_traversal(root.left)
postorder_traversal(root.right)
print(root.data, end=" ")
# Näidis kasutamine
root = Node(1)
root.left = Node(2)
root.right = Node(3)
root.left.left = Node(4)
root.left.right = Node(5)
print("Keskmine läbimine:")
inorder_traversal(root) # Väljund: 4 2 5 1 3
print("\nEesjärjestuses läbimine:")
preorder_traversal(root) # Väljund: 1 2 4 5 3
print("\nTagajärjestuses läbimine:")
postorder_traversal(root) # Väljund: 4 5 2 3 1
Iteratiivne DFS (pinuga)
DFS-i saab rakendada ka iteratiivselt, kasutades pinu. Siin on näide iteratiivsest eesjärjestuses läbimisest:
def iterative_preorder(root):
if root is None:
return
stack = [root]
while stack:
node = stack.pop()
print(node.data, end=" ")
# Lükka parem laps esimesena, et vasak laps töödeldaks esimesena
if node.right:
stack.append(node.right)
if node.left:
stack.append(node.left)
#Näidis kasutamine (sama puu nagu enne)
print("\nIteratiivne eesjärjestuses läbimine:")
iterative_preorder(root)
DFS-i kasutusjuhtumid
- Tee leidmine kahe sõlme vahel: DFS võib tõhusalt leida tee graafis või puus. Mõelge andmepakettide marsruutimisele üle võrgu (esindatud graafina). DFS võib leida marsruudi kahe serveri vahel, isegi kui on mitu marsruuti.
- Topoloogiline sorteerimine: DFS-i kasutatakse suunatud atsükliliste graafikute (DAG) topoloogiliseks sorteerimiseks. Kujutage ette ülesannete ajastamist, kus mõned ülesanded sõltuvad teistest. Topoloogiline sorteerimine korraldab ülesanded järjekorras, mis austab neid sõltuvusi.
- Tsüklite tuvastamine graafis: DFS saab tuvastada tsükleid graafis. Tsükli tuvastamine on oluline ressursside jaotamisel. Kui protsess A ootab protsessi B ja protsess B ootab protsessi A, võib see põhjustada ummikseisu.
- Labürintide lahendamine: DFS-i saab kasutada labürindi läbimiseks tee leidmiseks.
- Avaldiste parsimine ja hindamine: Kompilaatorid kasutavad DFS-põhiseid lähenemisviise matemaatiliste avaldiste parsimiseks ja hindamiseks.
DFS-i eelised ja puudused
Eelised:
- Lihtne rakendada: Rekursiivne rakendamine on sageli väga lühike ja kergesti mõistetav.
- Mäluefektiivne teatud puude puhul: DFS nõuab vähem mälu kui BFS sügavalt pesastatud puude puhul, kuna see peab salvestama ainult sõlmed praegusel teel.
- Saab lahendusi kiiresti leida: Kui soovitud lahendus on puu sügavuses, võib DFS selle leida kiiremini kui BFS.
Puudused:
- Ei garanteeri lühimat teed: DFS võib leida tee, kuid see ei pruugi olla lühim tee.
- Potentsiaalne lõpmatute silmuste jaoks: Kui puu pole hoolikalt struktureeritud (nt sisaldab tsükleid), võib DFS jääda lõpmatute silmustesse.
- Pinu ületäitumine: Rekursiivne rakendamine võib väga sügavate puude puhul põhjustada pinu ületäitumise vigu.
Laiutiotsing (BFS)
Laiutiotsing (BFS) on puu läbimise algoritm, mis uurib kõiki naabersõlmi praegusel tasemel enne järgmise taseme sõlmedele liikumist. See uurib puud taseme kaupa, alustades juurest. BFS-i rakendatakse tavaliselt iteratiivselt, kasutades järjekorda.
BFS Algoritm
- Pane juursõlm järjekorda.
- Kui järjekord pole tühi:
- Eemalda sõlm järjekorrast.
- Külasta sõlme (nt prindi selle väärtus).
- Pane kõik sõlme lapsed järjekorda.
Rakendamise näide (Python)
from collections import deque
def bfs_traversal(root):
if root is None:
return
queue = deque([root])
while queue:
node = queue.popleft()
print(node.data, end=" ")
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
#Näidis kasutamine (sama puu nagu enne)
print("BFS läbimine:")
bfs_traversal(root) # Väljund: 1 2 3 4 5
BFS-i kasutusjuhtumid
- Lühima tee leidmine: BFS garanteerib lühima tee leidmise kahe sõlme vahel kaalumata graafis. Kujutage ette sotsiaalvõrgustike saite. BFS saab leida lühima ühenduse kahe kasutaja vahel.
- Graafi läbimine: BFS-i saab kasutada graafi läbimiseks.
- Veebi roomamine: Otsingumootorid kasutavad BFS-i veebi roomamiseks ja lehekĂĽlgede indekseerimiseks.
- Lähimate naabrite leidmine: Geograafilises kaardistamises saab BFS leida lähimad restoranid, bensiinijaamad või haiglad antud asukohale.
- Üleujutuse täitmise algoritm: Pilditöötluses moodustab BFS aluse üleujutuse täitmise algoritmidele (nt tööriist "värvipütt").
BFS-i eelised ja puudused
Eelised:
- Garanteeritud lĂĽhima tee leidmine: BFS leiab alati lĂĽhima tee kaalumata graafis.
- Sobib lähimate sõlmede leidmiseks: BFS on tõhus sõlmede leidmiseks, mis on lähtesõlmele lähedal.
- Väldib lõpmatuid silmuseid: Kuna BFS uurib taseme kaupa, väldib see lõpmatutesse silmustesse sattumist, isegi tsüklitega graafides.
Puudused:
- Mälumahukas: BFS võib nõuda palju mälu, eriti laiade puude puhul, kuna see peab salvestama kõik praeguse taseme sõlmed järjekorras.
- Võib olla aeglasem kui DFS: Kui soovitud lahendus on puu sügavuses, võib BFS olla aeglasem kui DFS, kuna see uurib kõiki sõlmi igal tasemel enne sügavamale minekut.
DFS-i ja BFS-i võrdlemine
Siin on tabel, mis võtab kokku peamised erinevused DFS-i ja BFS-i vahel:
| Funktsioon | SĂĽgavutiotsing (DFS) | Laiutiotsing (BFS) |
|---|---|---|
| Läbimise järjekord | Uurib nii kaugele kui võimalik mööda iga haru enne tagasipöördumist | Uurib kõiki naabersõlmi praegusel tasemel enne järgmisele tasemele liikumist |
| Rakendamine | Rekursiivne või iteratiivne (pinuga) | Iteratiivne (järjekorraga) |
| Mälu kasutamine | Üldiselt vähem mälu (sügavate puude puhul) | Üldiselt rohkem mälu (laiade puude puhul) |
| LĂĽhim tee | Ei garanteeri lĂĽhima tee leidmist | Garanteeritud lĂĽhima tee leidmine (kaalumata graafides) |
| Kasutusjuhtumid | Tee leidmine, topoloogiline sorteerimine, tsükli tuvastamine, labürindi lahendamine, avaldiste parsimine | Lühima tee leidmine, graafi läbimine, veebi roomamine, lähimate naabrite leidmine, üleujutuse täitmine |
| Lõpmatute silmuste oht | Suurem oht (nõuab hoolikat struktureerimist) | Väiksem oht (uurib taseme kaupa) |
DFS-i ja BFS-i vahel valimine
Valik DFS-i ja BFS-i vahel sõltub konkreetsest probleemist, mida proovite lahendada, ja puu või graafi omadustest, millega töötate. Siin on mõned juhised, mis aitavad teil valida:
- Kasutage DFS-i, kui:
- Puu on väga sügav ja kahtlustate, et lahendus on sügaval all.
- Mälu kasutamine on suur probleem ja puu pole liiga lai.
- Peate tuvastama tsĂĽklid graafis.
- Kasutage BFS-i, kui:
- Peate leidma lĂĽhima tee kaalumata graafis.
- Peate leidma lähimad sõlmed lähtesõlmele.
- Mälu ei ole suur piirang ja puu on lai.
Lisaks binaarpuudele: DFS ja BFS graafides
Kuigi oleme peamiselt arutanud DFS-i ja BFS-i puude kontekstis, on need algoritmid võrdselt kohaldatavad graafidele, mis on üldisemad andmestruktuurid, kus sõlmedel võivad olla suvalised ühendused. Põhiprintsiibid jäävad samaks, kuid graafikud võivad sisaldada tsükleid, mis nõuavad täiendavat tähelepanu lõpmatute silmuste vältimiseks.
DFS-i ja BFS-i rakendamisel graafidele on tavaline säilitada "külastatud" komplekti või massiivi, et jälgida sõlmi, mida on juba uuritud. See takistab algoritmi sõlmede uuesti külastamist ja tsüklitesse sattumist.
Järeldus
Sügavutiotsing (DFS) ja laiutiotsing (BFS) on fundamentaalsed puu- ja graafiläbimise algoritmid, millel on erinevad omadused ja kasutusjuhtumid. Nende põhimõtete, rakendamise ja jõudluse kompromisside mõistmine on oluline igale arvutiteadlasele või tarkvarainsenerile. Konkreetset probleemi hoolikalt kaaludes saate valida sobiva algoritmi selle tõhusaks lahendamiseks. Kuigi DFS paistab silma mäluefektiivsuse ja sügavate harude uurimisega, garanteerib BFS lühima tee leidmise ja väldib lõpmatuid silmuseid, mistõttu on oluline mõista nende vahelisi erinevusi. Nende algoritmide valdamine suurendab teie probleemide lahendamise oskusi ja võimaldab teil enesekindlalt toime tulla keerukate andmestruktuuride väljakutsetega.